package com.virtual.repository.common.managers.concurrent;

import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import com.virtual.repository.common.criteria.Criterion;
import com.virtual.repository.common.managers.QueryParser;
import com.virtual.repository.common.model.Mapping;
import com.virtual.repository.common.model.ModelConfiguration;
import com.virtual.repository.common.queries.Query;

/**
 * Class responsible for translating the entire query to the entity's names 
 * and parsing the result entities to the object's names.
 * @author kchaber
 *
 */
public class ConcurrentQueryParser extends QueryParser {
	
	@Override
	public Query translateQuery(Query query, ModelConfiguration modelConfig) throws Exception {
		if (query == null) {
			throw new IllegalStateException("Query cannot be null");
		}
		
		Mapping parentMapping = modelConfig.getMapping(query.getSelectedClass().getName());
		if (parentMapping == null) {
			throw new IllegalStateException("Mapping for class: " + query.getSelectedClass().getName() + " not found");
		}
		
		ExecutorService executor = Executors.newCachedThreadPool();
	
		//rewrite selection fields
		Future<List<String>> futureSelectionFields = executor.submit(new SelectionFieldsTranslator(query.getSelectedFields(), parentMapping, modelConfig));
		
		//rewrite selection criteria
		Future<List<Criterion>> futureSelectionCriteria = executor.submit(new SelectionCriteriaTranslator(query.getSelectionCriteria(), parentMapping, modelConfig));
		
		//rewrite order by clauses
		Future<Map<String, Boolean>> futureOrderByCluases = executor.submit(new OrderByClausesTranslator(query.getOrderByMap(), parentMapping, modelConfig));
		
		//wait for results
		List<String> selectionFields = futureSelectionFields.get();
		List<Criterion> selectionCriteria = futureSelectionCriteria.get();
		Map<String, Boolean> orderByCluases = futureOrderByCluases.get();
		
		//construct the adjusted(translated) query
		Query adjustedQuery = new Query(query.getSelectedClass(), selectionFields, selectionCriteria, query.getLimit(), orderByCluases, parentMapping.getDbEntityName());
		
		System.out.println("Translated query: " + adjustedQuery);
		return adjustedQuery;
	}
	
	private class SelectionFieldsTranslator implements Callable<List<String>> {

		private List<String> fieldsToTranslate; 
		private Mapping parentMapping; 
		private ModelConfiguration modelConfig;
		
		public SelectionFieldsTranslator(List<String> fieldsToTranslate, Mapping parentMapping, ModelConfiguration modelConfig) {
			this.fieldsToTranslate = fieldsToTranslate;
			this.parentMapping = parentMapping;
			this.modelConfig = modelConfig;
		}

		@Override
		public List<String> call() throws Exception {
			return translateSelectionFields(fieldsToTranslate, parentMapping, modelConfig);
		}
	}
	
	private class SelectionCriteriaTranslator implements Callable<List<Criterion>> {

		private List<Criterion> criteriaToTranslate; 
		private Mapping parentMapping; 
		private ModelConfiguration modelConfig;
		
		public SelectionCriteriaTranslator(List<Criterion> criteriaToTranslate, Mapping parentMapping, ModelConfiguration modelConfig) {
			this.criteriaToTranslate = criteriaToTranslate;
			this.parentMapping = parentMapping;
			this.modelConfig = modelConfig;
		}

		@Override
		public List<Criterion> call() throws Exception {
			return translateSelectionCriteria(criteriaToTranslate, parentMapping, modelConfig);
		}
	}
	
	private class OrderByClausesTranslator implements Callable<Map<String, Boolean>> {

		private Map<String, Boolean> orderByClausesToTranslate; 
		private Mapping parentMapping; 
		private ModelConfiguration modelConfig;
		
		public OrderByClausesTranslator(Map<String, Boolean> orderByClausesToTranslate, Mapping parentMapping, ModelConfiguration modelConfig) {
			this.orderByClausesToTranslate = orderByClausesToTranslate;
			this.parentMapping = parentMapping;
			this.modelConfig = modelConfig;
		}

		@Override
		public Map<String, Boolean> call() throws Exception {
			return translateOrderByClauses(orderByClausesToTranslate, parentMapping, modelConfig);
		}
	}
}
